﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Threading;
using System.Workflow.Activities;
using System.Workflow.Runtime;
using Bookvar.ApplicationModel;
using Bookvar.Common;
using Bookvar.UpdateProcessWorkflow;
using BookvarWpf.Windows;
using BookvarSettings = BookvarWpf.Properties.Settings;
using LocalizationResources = Bookvar.UI.Localization.Resources;


namespace Bookvar
{
	[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
	public class UpdateProcessCommunicationService : IUpdateProcessCommunicationService
	{
		[SuppressMessage ("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
		protected delegate bool ShowYesNoMessageDelegate(string message, bool isProgressVisible, MessageBoxButtons buttons);

		private delegate void ShowProgressDelegate( string message );

		private delegate void ChangeProressDelegate( double message );

		private delegate void HideWindowDelegate();

		private delegate void CloseApplicationDelegate();

		private readonly DateTime defaultLastUpdateTime = new DateTime( 2008, 1, 1 );

		public Guid InstanceId { get; set; }
		public WorkflowInstance Instance { get; set; }
		protected Window MainWindow { get; private set; }
		private BookvarVersionManifest manifest;
		private string installTempFileName;
		private UpdateWindow updateWindow = new UpdateWindow();
		private WebClient downloadClient;
		private readonly EventWaitHandle asyncDownloadHandle = new EventWaitHandle( false, EventResetMode.ManualReset );

		public UpdateProcessCommunicationService()
		{
			MainWindow = BookvarApplication.Current.MainWindow;
		}

		#region IUpdateProcessCommunicationService Members

		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		public virtual void CheckIfUpdateCheckIsNecessary()
		{
			bool shouldCheckForUpdates;
			bool autoCheckForUpdates = BookvarSettings.Default.AutoCheckForUpdates;

			if ( autoCheckForUpdates )
			{
				try
				{
					DateTime lastUpdateCheckTime = BookvarSettings.Default.LastUpdateCheckTime;

					if ( lastUpdateCheckTime == defaultLastUpdateTime )
					{
						shouldCheckForUpdates = true;
					}
					else
					{
						TimeSpan timeSinceLastCheck = DateTime.Now - lastUpdateCheckTime;
						TimeSpan updateCheckInterval = BookvarSettings.Default.UpdateCheckInterval;
						shouldCheckForUpdates = ( timeSinceLastCheck > updateCheckInterval );
					}
				}
				catch
				{
					shouldCheckForUpdates = true;
				}
			}
			else
			{
				shouldCheckForUpdates = false;
			}

			if ( shouldCheckForUpdates )
			{
				ThreadPool.QueueUserWorkItem( delegate
				                              	{
				                              		Thread.Sleep( 200 );
				                              		OnUpdateCheckIsNecessary();
				                              	} );
				BookvarSettings.Default.LastUpdateCheckTime = DateTime.Now;
			}
			else
			{
				ThreadPool.QueueUserWorkItem( delegate
				                              	{
				                              		Thread.Sleep( 200 );
				                              		OnUpdateCheckIsNotNecessary();
				                              	} );
			}
		}

		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		public virtual void CheckForAvailableNewUpdates()
		{
			manifest = null;

			int retries = 2;

			while ( retries > 0 )
			{
				try
				{
					manifest = GetUpdatesManifest();
					retries = 0;
				}
				catch ( Exception )
				{
					--retries;
					if ( retries == 0 )
					{
						manifest = null;
					}
				}
			}

			if ( manifest != null )
			{
				Assembly current = Assembly.GetExecutingAssembly();
				Version currentVersion = current.GetName().Version;

				bool checkForBetas = BookvarSettings.Default.AutoCheckForBetaUpdates;

				bool updateAvailabe;

				if ( checkForBetas )
				{
					updateAvailabe = ( new Version( manifest.LatestBetaVersion ) > currentVersion ) &&
					                 !String.IsNullOrEmpty( manifest.LatestBetaVersionUrl );
				}
				else
				{
					updateAvailabe = ( new Version( manifest.LatestReleaseVersion ) > currentVersion ) &&
					                 !String.IsNullOrEmpty( manifest.LatestReleaseVersionUrl );
				}

				if ( updateAvailabe )
				{
					ThreadPool.QueueUserWorkItem( delegate
					                              	{
					                              		Thread.Sleep( 200 );
					                              		OnNewUpdatesAreAvailable();
					                              	} );
				}
				else
				{
					ThreadPool.QueueUserWorkItem( delegate
					                              	{
					                              		Thread.Sleep( 200 );
					                              		OnNoNewUpdatesAvailable();
					                              	} );
				}
			}
		}

		public virtual void ShouldDownloadUpdates()
		{
			bool result =
				(bool)
				MainWindow.Dispatcher.Invoke( DispatcherPriority.Input, new ShowYesNoMessageDelegate( ShowDialogMessage ),
				                              LocalizationResources.NewUpdatesAvailable, false,
				                              MessageBoxButtons.YesNo );
			if ( result )
			{
				ThreadPool.QueueUserWorkItem( delegate
				                              	{
				                              		Thread.Sleep( 200 );
				                              		OnProceedDownloadingUpdates();
				                              	} );
			}
			else
			{
				ThreadPool.QueueUserWorkItem( delegate
				                              	{
				                              		Thread.Sleep( 200 );
				                              		OnDoNotDownloadUpdates();
				                              	} );
			}
		}

		public virtual void DownloadUpdates()
		{
			installTempFileName = Path.GetTempFileName() + ".msi";
			MainWindow.Dispatcher.BeginInvoke( DispatcherPriority.Input, new ShowProgressDelegate( ShowProgress ),
			                                   LocalizationResources.DownloadInProgress );
			OnProgress( 0.0 );

			string downloadUriString = ( BookvarSettings.Default.AutoCheckForBetaUpdates )
			                           	? manifest.LatestBetaVersionUrl
			                           	: manifest.LatestReleaseVersionUrl;

			if ( !String.IsNullOrEmpty( downloadUriString ) )
			{
				Uri downloadUri = new Uri( downloadUriString );
				using ( downloadClient = new WebClient() )
				{
					asyncDownloadHandle.Reset();
					downloadClient.DownloadProgressChanged += client_DownloadProgressChanged;
					downloadClient.DownloadFileCompleted += downloadClient_DownloadFileCompleted;
					downloadClient.DownloadFileAsync( downloadUri, installTempFileName );
					asyncDownloadHandle.WaitOne();
					if ( downloadClient.IsBusy )
					{
						Instance.Abort();
						downloadClient.CancelAsync();
					}
				}
			}
			MainWindow.Dispatcher.BeginInvoke( DispatcherPriority.Input, new HideWindowDelegate( HideWindow ) );
		}

		public virtual void ShouldInstallUpdates()
		{
			bool result =
				(bool)
				MainWindow.Dispatcher.Invoke( DispatcherPriority.Input, new ShowYesNoMessageDelegate( ShowDialogMessage ),
				                              LocalizationResources.StartInstallationOfUpdates, false, MessageBoxButtons.YesNo );
			if ( result )
			{
				ThreadPool.QueueUserWorkItem( delegate
				                              	{
				                              		Thread.Sleep( 200 );
				                              		OnProceedInstallingUpdates();
				                              		Thread.CurrentThread.Abort();
				                              	} );
			}
			else
			{
				ThreadPool.QueueUserWorkItem( delegate
				                              	{
				                              		Thread.Sleep( 200 );
				                              		OnDoNotInstallUpdates();
				                              		Thread.CurrentThread.Abort();
				                              	} );
			}
		}

		public virtual void InstallUpdates()
		{
			string strFriendlyName = AppDomain.CurrentDomain.FriendlyName;
			Process[] pro = Process.GetProcessesByName( strFriendlyName.Substring( 0, strFriendlyName.LastIndexOf( '.' ) ) );

			Shell.Execute( new WindowWrapper( pro[ 0 ].MainWindowHandle ), installTempFileName, String.Empty );
			MainWindow.Dispatcher.Invoke( DispatcherPriority.Input,
			                              new CloseApplicationDelegate( BookvarApplication.Current.CloseApplication ) );
		}

		public virtual void FinalizeUpdateCheckingProcess()
		{
//			MainWindow.Dispatcher.Invoke(DispatcherPriority.Input, new ShowYesNoMessageDelegate(ShowDialogMessage), "Update check process is finalizing.",false,MessageBoxButtons.OK );
		}

		public virtual void ErrorWhileExecutingProcess()
		{
//			MainWindow.Dispatcher.Invoke(DispatcherPriority.Input, new ShowYesNoMessageDelegate(ShowDialogMessage), "Error: ", false, MessageBoxButtons.OK);
		}

		public event EventHandler<ExternalDataEventArgs> UpdateCheckIsNecessary;

		public event EventHandler<ExternalDataEventArgs> UpdateCheckIsNotNecessary;

		public event EventHandler<ExternalDataEventArgs> NewUpdatesAreAvailable;

		public event EventHandler<ExternalDataEventArgs> NoNewUpdatesAvailable;

		public event EventHandler<ExternalDataEventArgs> ProceedDownloadingUpdates;

		public event EventHandler<ExternalDataEventArgs> DoNotDownloadUpdates;

		public event EventHandler<ExternalDataEventArgs> ProceedInstallingUpdates;

		public event EventHandler<ExternalDataEventArgs> DoNotInstallUpdates;

		#endregion

		private void downloadClient_DownloadFileCompleted( object sender, AsyncCompletedEventArgs e )
		{
			asyncDownloadHandle.Set();
		}

		protected virtual void OnUpdateCheckIsNotNecessary()
		{
			if ( UpdateCheckIsNotNecessary != null )
			{
				UpdateCheckIsNotNecessary( null, new ExternalDataEventArgs( InstanceId ) );
			}
		}

		protected virtual void OnUpdateCheckIsNecessary()
		{
			if ( UpdateCheckIsNecessary != null )
			{
				UpdateCheckIsNecessary( null, new ExternalDataEventArgs( InstanceId ) );
			}
		}

		protected virtual void OnNoNewUpdatesAvailable()
		{
			if ( NoNewUpdatesAvailable != null )
			{
				NoNewUpdatesAvailable( null, new ExternalDataEventArgs( InstanceId ) );
			}
		}

		protected virtual void OnNewUpdatesAreAvailable()
		{
			if ( NewUpdatesAreAvailable != null )
			{
				NewUpdatesAreAvailable( null, new ExternalDataEventArgs( InstanceId ) );
			}
		}

		protected virtual void OnDoNotDownloadUpdates()
		{
			if ( DoNotDownloadUpdates != null )
			{
				DoNotDownloadUpdates( null, new ExternalDataEventArgs( InstanceId ) );
			}
		}

		protected virtual void OnProceedDownloadingUpdates()
		{
			if ( ProceedDownloadingUpdates != null )
			{
				ProceedDownloadingUpdates( null, new ExternalDataEventArgs( InstanceId ) );
			}
		}

		protected virtual void OnProceedInstallingUpdates()
		{
			if ( ProceedInstallingUpdates != null )
			{
				ProceedInstallingUpdates( null, new ExternalDataEventArgs( InstanceId ) );
			}
		}

		protected virtual void OnDoNotInstallUpdates()
		{
			if ( DoNotInstallUpdates != null )
			{
				DoNotInstallUpdates( null, new ExternalDataEventArgs( InstanceId ) );
			}
		}

		protected bool ShowDialogMessage( string message, bool isProgressVisible, MessageBoxButtons buttons )
		{
			updateWindow = new UpdateWindow
			               	{
			               		UpdatingProcessText = message,
			               		IsProgressVisible = isProgressVisible,
			               		IsYesButtonVisible = buttons == MessageBoxButtons.YesNo,
			               		IsNoButtonVisible = buttons == MessageBoxButtons.YesNo,
			               		IsOkButtonVisible = buttons == MessageBoxButtons.OK,
			               		IsCancelButtonVisible = buttons == MessageBoxButtons.OKCancel
			               	};
			bool result = updateWindow.ShowDialog() ?? false;


			return result;
		}

//
		private void ShowProgress( string message )
		{
			updateWindow = new UpdateWindow
			               	{
			               		UpdatingProcessText = message,
			               		IsProgressVisible = true,
			               		IsYesButtonVisible = false,
			               		IsNoButtonVisible = false,
			               		IsOkButtonVisible = false,
			               		IsCancelButtonVisible = true
			               	};
			updateWindow.Show();
			updateWindow.Closed += updateWindow_Closed;
		}

		private void updateWindow_Closed( object sender, EventArgs e )
		{
			asyncDownloadHandle.Set();
		}

		private void HideWindow()
		{
			if ( updateWindow.IsVisible )
			{
				updateWindow.Close();
			}
		}

		private void client_DownloadProgressChanged( object sender, DownloadProgressChangedEventArgs e )
		{
			OnProgress( e.ProgressPercentage );
		}

		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		private static BookvarVersionManifest GetUpdatesManifest()
		{
			BookvarVersionManifest manifest = null;
			try
			{
				string versionsUrl = BookvarSettings.Default.VersionManifestUrl;

				WebClient client = new WebClient();
				string manifestText = client.DownloadString( versionsUrl );
				manifest = Serializer.Deserialize<BookvarVersionManifest>( manifestText );
			}
			catch ( Exception )
			{
			}
			return manifest;
		}

		protected void OnProgress( double percent )
		{
			MainWindow.Dispatcher.Invoke( DispatcherPriority.Input, new ChangeProressDelegate( ChangeProgress ), percent );
		}

		private void ChangeProgress( double percent )
		{
			if ( updateWindow.IsVisible )
			{
				updateWindow.ProgressValue = percent;
			}
		}
	}


	public class WindowWrapper : IWin32Window
	{
		public WindowWrapper( IntPtr handle )
		{
			_hwnd = handle;
		}

		public IntPtr Handle
		{
			get { return _hwnd; }
		}

		private readonly IntPtr _hwnd;
	}
}